package com.sniper.utils;

import org.apache.oro.text.regex.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

/**
 * 请设置好ffmpeg环境变量 参数参考
 * http://www.cuplayer.com/player/PlayerCode/FFmpeg/2014/1022/1629.html
 *
 * @author sniper
 */
public class FfmpegUtils {
    private static Logger logger = LoggerFactory.getLogger(FfmpegUtils.class);
    ///usr/local/bin/ffmpeg
    private String ffmpegPath = "ffmpeg";
    /**
     * 截取时间 比如 00:02:06 20 表示20秒 默认120秒
     */
    private String cutTime = "5";
    /**
     * 720x480 截屏大小如果为空将设置默认视频大小
     */
    private String cutWidthAndHeight = "";
    /**
     * 缩略图大小 720x480
     */
    private String thumbWidthAndHeight = "720x480";
    private VideoInfo videoInfo = new VideoInfo();
    //视频路径
    private String videoPath = "";
    //输出路径
    private String outPath = "";
    //视频信息
    private String videoData = "";
    private String imgSuffix = ".png";
    /**
     * 允许操作的视频格式
     */
    private static HashMap<String, String> fileType;

    static {
        fileType = new HashMap<>();
        fileType.put("avi", "true");
        fileType.put("mpg", "true");
        fileType.put("wmv", "true");
        fileType.put("3gp", "true");
        fileType.put("mov", "true");
        fileType.put("mp4", "true");
        fileType.put("asf", "true");
        fileType.put("asx", "true");
        fileType.put("flv", "true");
    }

    static class VideoInfo {
        // 提取出播放时间
        String playTime;
        // 开始时间
        String startTime;
        // bitrate 码率 单位
        String unitKb;
        // 编码格式
        String videoEncoding;
        // 视频格式
        String videoFormat;
        // 分辨率
        String videoResolution;
        String audioEncoding;
        // 音频采样频率
        String audioSample;


        @Override
        public String toString() {
            return "FileInfo [playTime=" + playTime + ", startTime="
                    + startTime + ", unitKb=" + unitKb + ", videoEncoding="
                    + videoEncoding + ", videoFormat=" + videoFormat
                    + ", videoResolution=" + videoResolution
                    + ", audioEncoding=" + audioEncoding + ", audioSample="
                    + audioSample + "]";
        }

    }


    /**
     * 数据初始化负责读取视频有关信息
     *
     * @param videoPath
     */
    public FfmpegUtils(String videoPath) {
        //this.initFfmpegPath();

        this.videoPath = videoPath;

        if (!checkContentType()) {
            throw new RuntimeException("不支持格式");
        }

        if (!videoPath.startsWith("http")) {
            File file = new File(this.videoPath);
            if (!file.exists()) {
                try {
                    throw new FileNotFoundException(videoPath + "文件找不到");
                } catch (FileNotFoundException e1) {
                    e1.printStackTrace();
                    logger.error(videoPath + "文件找不到");
                }
            }
        }
        this.initVideoInfo();

    }


    public FfmpegUtils setFfmpegPath(String ffmpegPath) {
        this.ffmpegPath = ffmpegPath;
        return this;
    }

    public FfmpegUtils setCutTime(String cutTime) {
        this.cutTime = cutTime;
        return this;
    }

    public FfmpegUtils setCutWidthAndHeight(String cutWidthAndHeight) {
        this.cutWidthAndHeight = cutWidthAndHeight;
        return this;
    }


    public FfmpegUtils setThumbWidthAndHeight(String thumbWidthAndHeight) {
        this.thumbWidthAndHeight = thumbWidthAndHeight;
        return this;
    }

    public FfmpegUtils setImgSuffix(String imgSuffix) {
        this.imgSuffix = imgSuffix;
        return this;
    }

    /**
     * 设置文件保存地址
     *
     * @param suffix
     * @return
     */
    public String getSavePath(String suffix) {
        String path = "";
        if (!ValidateUtil.isValid(videoPath)) {
            // 设置图片保存路径
            path = videoPath.substring(0, videoPath.lastIndexOf(".")) + "_" + getCutWidthAndHeight() + suffix;
        }
        return path;
    }

    public FfmpegUtils setOutPath(String outPath) {
        this.outPath = outPath;
        return this;
    }


    /**
     * 检查格式
     *
     * @return
     */
    private boolean checkContentType() {

        String type = videoPath.substring(videoPath.lastIndexOf(".") + 1, videoPath.length()).toLowerCase();
        // ffmpeg能解析的格式：（asx，asf，mpg，wmv，3gp，mp4，mov，avi，flv等）
        return "true".equals(fileType.get(type));
    }

    /**
     * 获取视频的时间长度秒计算
     *
     * @return
     * @throws MalformedPatternException
     */
    public float getPlayTimesSeconds() throws MalformedPatternException {
        float seconds = 0f;
        String times = videoInfo.playTime;
        String[] timess = times.split(":");
        float hoursSec = Float.valueOf(timess[0]) * 60 * 60;
        float minSec = Float.valueOf(timess[1]) * 60;
        float sec = Float.valueOf(timess[2]);
        seconds = hoursSec + minSec + sec;
        return seconds;

    }

    /**
     * 获取视频的宽度X高度
     *
     * @return
     * @throws
     */
    private String getCutWidthAndHeight() {
        if (cutWidthAndHeight.equals("")) {
            cutWidthAndHeight = videoInfo.videoResolution;
        }
        return cutWidthAndHeight;
    }

    /**
     * http://www.cnblogs.com/wangkangluo1/archive/2012/07/10/2585095.html 转换命令：
     * ffmpeg -i "20090401010.mp4" -y -ab 32 -ar 22050 -qscale 10 -s 640*480 -r
     * 15 /opt/a.flv
     * <p>
     * -i 是 要转换文件名 -y是 覆盖输出文件 -ab 是 音频数据流，大家在百度听歌的时候应该都可以看到 128 64 -ar 是 声音的频率
     * 22050 基本都是这个。 -qscale 是视频输出质量，后边的值越小质量越高，但是输出文件就越“肥” -s 是输出 文件的尺寸大小！ -r 是
     * 播放侦数。 ffmpeg能解析的格式：（asx，asf，mpg，wmv，3gp，mp4，mov，avi，flv等） -aspect aspect
     * 设置横纵比 4:3 16:9 或 1.3333 1.7777 1280X720 720X480 800x480 1024x576
     * 1920x1080 320x240
     *
     * @return
     * @throws MalformedPatternException
     * @throws IOException
     * @throws InterruptedException
     */
    public String videoToFlv() throws MalformedPatternException, IOException, InterruptedException {

        List<String> commands = new ArrayList<>();

        String flvPath = getSavePath(".flv");
        commands.add(ffmpegPath);
        commands.add("-i");
        commands.add(videoPath);
        // 音频bit，默认比较小 一般选择 32，64，96，128
        commands.add("-ab");
        commands.add("64K");
        // 使用的音频
        // commands.add("-acodec");
        // commands.add("mp4");
        // 设置声道数，1就是单声道，2就是立体声
        commands.add("-ac");
        commands.add("2");
        // 增大一倍音量
        commands.add("-vol");
        commands.add("512");
        // 音频采集率默认24000
        commands.add("-ar");
        commands.add("22050");
        // 指定比特率(bits/s)，似乎ffmpeg是自动VBR的，指定了就大概是平均比特率
        // bitrate 设置比特率，缺省200kb/s
        // commands.add("-b:v");
        // commands.add("200K");
        // 帧数，一般29.97 flv设置15,无论怎么设都出现past duration too large
        commands.add("-r");
        commands.add("12");
        // 以<数值>质量为基础的VBR，取值0.01-255，约小质量越好 == -qscale
        commands.add("-q");
        commands.add("4");
        // 设置格式
        commands.add("-f");
        commands.add("flv");
        // 设置生成的宽高
        // commands.add("-s");
        // commands.add(getCutWidthAndHeight());
        // 设置文件输出
        commands.add("-y");
        commands.add(flvPath);

        logger.debug(commands.toString());
        BufferedReader reader = null;

        try {
            ProcessBuilder builder = new ProcessBuilder();
            builder.command(commands);
            Process process = builder.start();
            StringBuilder result = new StringBuilder();
            reader = new BufferedReader(new InputStreamReader(
                    process.getInputStream()));
            String line;
            while ((line = reader.readLine()) != null) {
                result.append(line);
            }
            process.waitFor();
            int exit = process.exitValue();
            if (exit != 0) {
                throw new IOException("failed to execute:" + builder.command()
                        + " with result:" + result);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (reader != null) {
                reader.close();
            }
        }
        return flvPath;
    }

    /**
     * 转mp4 / cuplayer.com ffmpeg能解析的格式：（asx，asf，mpg，wmv，3gp，mp4，mov，avi，flv等）
     * http://ferreousbox.iteye.com/blog/163865
     *
     * @return
     * @throws MalformedPatternException
     * @throws IOException
     * @throws InterruptedException
     */
    public void videoToMp4() throws MalformedPatternException, IOException, InterruptedException {

        List<String> commands = new ArrayList<>();
        //
        String flvPath = getSavePath(".mp4");
        commands.add(ffmpegPath);
        commands.add("-i");
        commands.add(videoPath);
        // 音频bit，默认比较小 一般选择 32，64，96，128
        commands.add("-ab");
        commands.add("64K");
        // 音频采集率默认24000
        commands.add("-ar");
        commands.add("24000");
        commands.add("-b:v");
        commands.add("300k");
        // 使用的音频
        commands.add("-acodec");
        commands.add("mp3");
        // 增大一倍音量
        commands.add("-vol");
        commands.add("512");
        // 以<数值>质量为基础的VBR，取值0.01-255，约小质量越好 == -qscale
        commands.add("-q");
        commands.add("4");
        commands.add("-vcodec");
        commands.add("copy");

        // 设置生成的宽高
        // commands.add("-s");
        // commands.add(getCutWidthAndHeight());
        // 设置文件输出
        commands.add("-y");
        commands.add(flvPath);
        //System.out.println(commands.toString().replace(",", ""));
        logger.debug(commands.toString());
        BufferedReader reader = null;

        try {
            JavaShellUtil.executeShell(commands.toString());

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 视频截图
     *
     * @return
     * @throws MalformedPatternException
     */
    public void videoScreenshot() throws MalformedPatternException {

        List<String> commands = new ArrayList<>();
        commands.add(ffmpegPath);
        commands.add("-ss");
        //截取的时间段
        if (ValidateUtil.isValid(this.cutTime)) {
            commands.add(this.cutTime);// 这个参数是设置截取视频多少秒时的画面
        } else {
            commands.add("10");
        }
        commands.add("-i");
        commands.add(videoPath);

        // 一般配合 -t表示每次的范围
        // commands.add("-t");
        // commands.add("0.001");
        // 配置表表达式生成多张,文件名字后面加上%03d表示生成1000张，0-999
        // commands.add("-r");
        // commands.add("1");
        commands.add("-s");
        commands.add(thumbWidthAndHeight);
        commands.add("-f");
        commands.add("image2");
        commands.add("-y");
        commands.add(outPath);
        logger.debug(commands.toString());
        try {
            ProcessBuilder builder = new ProcessBuilder();
            builder.command(commands);
            builder.start();
        } catch (Exception e) {
            e.printStackTrace();
        }

    }


    /**
     * ffmpeg能解析的格式：（asx，asf，mpg，wmv，3gp，mp4，mov，avi，flv等） 获取视频文件信息
     *
     * @return
     */
    private void initVideoInfo() {

        List<String> commend = new ArrayList<>();

        commend.add(ffmpegPath);
        commend.add("-i");
        commend.add(videoPath);

        ProcessBuilder builder = new ProcessBuilder();
        builder.command(commend);
        builder.redirectErrorStream(true);
        Process p;
        try {
            p = builder.start();
            int ret = p.waitFor();// 这里线程阻塞，将等待外部转换进程运行成功运行结束后，才往下执行
            // 1. start
            BufferedReader buf; // 保存ffmpeg的输出结果流

            StringBuffer sb = new StringBuffer();
            // read the standard output
            // ret == 1
            String line;
            if (ret == 1) {
                buf = new BufferedReader(new InputStreamReader(p.getInputStream()));
                sb = new StringBuffer();
                while ((line = buf.readLine()) != null) {
                    sb.append(line);
                }
                buf.close();
            }
            videoData = sb.toString();
            getVideoInfo();
            // 1. end
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取文件信息
     */
    private void getVideoInfo() {
        PatternCompiler compiler = new Perl5Compiler();
        try {
            String regexDuration = "Duration: (.*?), start: (.*?), bitrate: (\\d*) kb\\/s";
            String regexVideo = "Video: (.*?), (.*?), (.*?)[,\\s]";
            String regexAudio = "Audio: (\\w*), (\\d*) Hz";

            Pattern patternDuration = compiler.compile(regexDuration, Perl5Compiler.CASE_INSENSITIVE_MASK);
            Perl5Matcher matcherDuration = new Perl5Matcher();
            if (matcherDuration.contains(videoData, patternDuration)) {
                MatchResult re = matcherDuration.getMatch();
                videoInfo.playTime = re.group(1);
                videoInfo.startTime = re.group(2);
                videoInfo.unitKb = re.group(3);
            }

            Pattern patternVideo = compiler.compile(regexVideo, Perl5Compiler.CASE_INSENSITIVE_MASK);
            Perl5Matcher matcherVideo = new Perl5Matcher();

            if (matcherVideo.contains(videoData, patternVideo)) {
                MatchResult re = matcherVideo.getMatch();
                videoInfo.videoEncoding = re.group(1);
                videoInfo.videoFormat = re.group(2);
                videoInfo.videoResolution = re.group(3);

            }

            Pattern patternAudio = compiler.compile(regexAudio, Perl5Compiler.CASE_INSENSITIVE_MASK);
            Perl5Matcher matcherAudio = new Perl5Matcher();

            if (matcherAudio.contains(videoData, patternAudio)) {
                MatchResult re = matcherAudio.getMatch();
                videoInfo.audioEncoding = re.group(1);
                videoInfo.audioSample = re.group(2);
            }
        } catch (MalformedPatternException e) {
            e.printStackTrace();
        }
    }

    /**
     * 下面是原理子 已经被拆的不能使用了
     *
     * @param args
     */
    public void demo(String[] args) {

        PatternCompiler compiler = new Perl5Compiler();
        try {
            String regexDuration = "Duration: (.*?), start: (.*?), bitrate: (\\d*) kb\\/s";
            String regexVideo = "Video: (.*?), (.*?), (.*?)[,\\s]";
            String regexAudio = "Audio: (\\w*), (\\d*) Hz";

            Pattern patternDuration = compiler.compile(regexDuration, Perl5Compiler.CASE_INSENSITIVE_MASK);
            Perl5Matcher matcherDuration = new Perl5Matcher();
            // System.out.println(result);
            if (matcherDuration.contains(videoData, patternDuration)) {
                MatchResult re = matcherDuration.getMatch();

                System.out.println("提取出播放时间  ===" + re.group(1));
                System.out.println("开始时间        =====" + re.group(2));
                System.out.println("bitrate 码率 单位 kb==" + re.group(3));
            }

            Pattern patternVideo = compiler.compile(regexVideo,
                    Perl5Compiler.CASE_INSENSITIVE_MASK);
            Perl5Matcher matcherVideo = new Perl5Matcher();

            if (matcherVideo.contains(videoData, patternVideo)) {
                MatchResult re = matcherVideo.getMatch();
                System.out.println("编码格式  ===" + re.group(1));
                System.out.println("视频格式 ===" + re.group(2));
                System.out.println(" 分辨率  == =" + re.group(3));
            }

            Pattern patternAudio = compiler.compile(regexAudio,
                    Perl5Compiler.CASE_INSENSITIVE_MASK);
            Perl5Matcher matcherAudio = new Perl5Matcher();

            if (matcherAudio.contains(videoData, patternAudio)) {
                MatchResult re = matcherAudio.getMatch();
                System.out.println("音频编码             ===" + re.group(1));
                System.out.println("音频采样频率  ===" + re.group(2));
            }

        } catch (MalformedPatternException e) {
            e.printStackTrace();
        }
    }

    /**
     * 截图
     *
     * @param video_path
     * @return
     */
    public boolean processImg(String video_path) {

        List<String> commands = new ArrayList<>();
        commands.add(ffmpegPath);
        if (ValidateUtil.isValid(cutTime)) {
            commands.add("-ss");
            commands.add(cutTime);// 这个参数是设置截取视频多少秒时的画面
        }
        commands.add("-i");
        commands.add(video_path);
        commands.add("-y");
        commands.add("-f");
        commands.add("image2");
        commands.add("-t");
        commands.add("0.0001");
        commands.add("-s");
        commands.add(getCutWidthAndHeight());
        commands.add(outPath);
        try {
            ProcessBuilder builder = new ProcessBuilder();
            builder.command(commands);
            builder.start();
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    public static void main(String[] args) throws MalformedPatternException,
            IOException, InterruptedException {
        // /Users/suzhen/approot/Tomcat/apache-tomcat-8.0.28/webapps/vedio/attachments/image
        String mp4path = "/Users/suzhen/approot/www/zhuanti/2017zhongyao/01.flv";
        // String flvpath =
        // "/Users/suzhen/approot/Tomcat/apache-tomcat-8.0.28/webapps/video/attachments/image/20160311/0d974103-8a67-4070-a2d5-1c713f269222.mp4";

        FfmpegUtils ffmpegUtils = new FfmpegUtils(mp4path);

        // ffmpegUtils.getCutWidthAndHeight();
        // String string = ffmpegUtils.getPlayTimes();
        // ffmpegUtils.videoScreenshot();
        // System.out.println(ffmpegUtils.getPlayTimesSeconds());
        // System.out.println(string);
        ffmpegUtils.setOutPath("").videoScreenshot();

    }

}
